package uk.ac.nott.cs.g53dia;

import java.util.*;

/**
 * An infinite grid of cells representing the current state of the environment
 * at a given timestep.
 * <p>
 * Each cell in the Environment implements the {@link Cell} interface.
 * 
 * @author Neil Madden.
 */
/*
 * Copyright (c) 2003 Stuart Reeves. Copyright (c) 2003-2005 Neil Madden.
 * 
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */
public class Environment {
	/**
	 * Mapping from an (x,y) point to a Cell.
	 */
	private Map map;
	/**
	 * Mapping from an (x,y) point to a Bee.
	 */
	private Map beeMap;
	/**
	 * CellFactory which is called to generate new environment.
	 */
	private CellFactory cfactory;
	/**
	 * Current timestep of the simulation.
	 */
	private long timestep;
	/**
	 * Length of a day in time-steps.
	 */
	public final static int DAY_LENGTH = 10000;

	private static Environment INSTANCE;

	/**
	 * Initialises the bee environment.
	 * 
	 * Creates an initial environment of flowers.
	 * 
	 * @param size
	 *            size of the initial environment
	 * @param factory
	 *            factory used to create new cells
	 */
	private Environment(int size, CellFactory factory) {
		this.map = new HashMap();
		this.beeMap = new HashMap();
		this.cfactory = factory;
		this.timestep = 0l;

		// Generate initial environment
		for (int x = -size; x <= size; x++) {
			for (int y = -size; y <= size; y++) {
				cfactory.generateCell(this, new Point(x, y));
			}
		}
	}

	private Environment(int size) {
		this(size, new DefaultCellFactory());
	}

	public static synchronized Environment getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new Environment(30);
		} 
		return INSTANCE;
	}

	/**
	 * Return the cells a bee can see.
	 * 
	 * @param pos
	 *            position of the bee
	 * @param size
	 *            distance the bee can see
	 * @return an array containing the cells visible from pos.
	 */
	public synchronized Cell[][] getView(Point pos, int size) {
		Cell[][] res = new Cell[size * 2 + 1][size * 2 + 1];
		for (int x = pos.x - size; x <= pos.x + size; x++) {
			for (int y = pos.y - size; y <= pos.y + size; y++) {
				int i = x - (pos.x - size);
				int j = (2 * size) - (y - (pos.y - size));
				res[i][j] = getCell(new Point(x, y));
			}
		}
		return res;
	}

	/**
	 * Get the cell at a specified location
	 * 
	 * @param pos
	 *            coordinates of the cell
	 */
	public Cell getCell(Point pos) {
		if (!map.containsKey(pos))
			// No cell at this position, so make a new one
			cfactory.generateCell(this, pos);
		return (Cell) map.get(pos);
	}

	/**
	 * Get the current timestep.
	 * 
	 * @return the current timestep
	 */
	public long getTimestep() {
		return timestep;
	}

	/**
	 * Increase timestep.
	 */
	public void tick() {
		timestep++;
	}

	/**
	 * Add a cell to the environment.
	 * 
	 * @param c
	 *            cell to be added
	 */
	public synchronized void putCell(Cell c) {
		map.put(c.getPoint(), c);
	}

	public static synchronized void moveBee(Bee bee) {
//		System.out.println(bee + "aaa"+bee.getPosition());
		getInstance().beeMap.put(bee, bee.getPosition());
//		System.out.println(getInstance().beeMap);
	}
	
	public synchronized List<Point> getVisibleBees(Point position, int viewRange) {
		List<Point> result = new ArrayList<Point>();
		for (Object point : getInstance().beeMap.values()) {
			Point p = (Point) point;
			if (!p.equals(position) && Math.abs(p.x-position.x) <= viewRange && Math.abs(p.y-position.y) <= viewRange) {
				result.add((Point) p.clone());
			}
		}
		return result;
	}

}
